package protocol

import (
	"bytes"
	"io"
	"strings"
)

const (
	SEGMENT_BITS uint32 = 0x7f
	CONTINUE_BIT uint32 = 0x80
)

func ReadVarInt(r io.Reader) (int, error) {
	value := int32(0)
	position := int32(0)
	var currentByte [1]byte

	for {
		if _, err := r.Read(currentByte[:]); err != nil {
			return 0, err
		}

		value |= int32((uint32(currentByte[0]) & SEGMENT_BITS) << position)

		if currentByte[0]&byte(CONTINUE_BIT) == 0 {
			break
		}

		position += 7
	}

	return int(value), nil
}

func WriteVarInt(w io.Writer, n int) (int64, error) {
	totalWritten := int64(0)
	value := uint32(n)

	for {
		if (value & ^SEGMENT_BITS) == 0 {
			r := bytes.NewReader([]byte{byte(value)})
			n, err := io.Copy(w, r)
			totalWritten += n
			if err != nil {
				return totalWritten, err
			}

			return totalWritten, nil
		}

		r := bytes.NewReader([]byte{byte((value & SEGMENT_BITS) | CONTINUE_BIT)})
		n, err := io.Copy(w, r)
		totalWritten += n
		if err != nil {
			return totalWritten, err
		}

		value >>= 7
	}
}

func ReadString(r io.Reader) (string, error) {
	length, err := ReadVarInt(r)
	if err != nil {
		return "", err
	}

	var stringBuff bytes.Buffer
	if _, err := io.CopyN(&stringBuff, r, int64(length)); err != nil {
		return "", err
	}

	return stringBuff.String(), nil
}

func WriteString(r io.Writer, str string) (int64, error) {
	totalWritten := int64(0)

	n, err := WriteVarInt(r, len(str))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	n, err = io.Copy(r, strings.NewReader(str))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	return totalWritten, nil
}
